package com.symaster.common.geom.util;

import org.opencv.core.Mat;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.ServerSocket;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author yinmiao
 * @version 2021-04-10 10:24
 */
public class IOUtil {

    public static byte[] readFileOfByteArray(File file) {
        if (!file.isFile()) {
            throw new IllegalArgumentException("文件不存在");
        }
        ByteArrayOutputStream byteArrayOutputStream = null;
        FileInputStream fileInputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            fileInputStream = new FileInputStream(file);
            write(fileInputStream, byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            close(byteArrayOutputStream, fileInputStream);
        }
    }

    /**
     * 随机获取一个未被占用的端口
     * 范围: 10000 - 20000
     */
    public static int randomPort() {
        ServerSocket serverSocket = null;
        try {
            int i = new Random().nextInt(9999) + 10000;
            serverSocket = new ServerSocket(i);
            return i;
        } catch (Exception e) {
            return randomPort();
        } finally {
            IOUtil.close(serverSocket);
        }
    }

    public static byte[] toByteArray(InputStream is) {
        try {
            if (is == null || is.available() <= 0) {
                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] cache = new byte[4096];

        try {
            int i;
            while ((i = is.read(cache)) > 0) {
                bos.write(cache, 0, i);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            close(is);
        }

        return bos.toByteArray();
    }

    public static void write(InputStream in, OutputStream os) throws IOException {
        int i;
        byte[] bytes = new byte[2048];
        while ((i = in.read(bytes, 0, bytes.length)) != -1) {
            os.write(bytes, 0, i);
        }
    }

    /**
     * 输出
     */
    public static void write(BufferedInputStream bis, BufferedOutputStream bos) throws IOException {
        write(bis, bos, 4096);
    }

    /**
     * 输出
     *
     * @param bis          输入流
     * @param bos          输出流
     * @param bufferedSize 缓存大小 - 字节
     */
    public static void write(BufferedInputStream bis, BufferedOutputStream bos, int bufferedSize) throws IOException {
        byte[] bytes = new byte[bufferedSize];
        int i;
        while ((i = bis.read(bytes, 0, bytes.length)) != -1) {
            bos.write(bytes, 0, i);
        }
    }

    /**
     * 释放资源
     */
    public static void close(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                if (closeable != null) {
                    try {
                        closeable.close();
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    }

    public static void close(Mat... mats) {
        if (mats != null) {
            for (Mat mat : mats) {
                if (mat != null) {
                    try {
                        mat.release();
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    }

    public static <T extends Mat> void close(List<T> mats) {
        if (mats != null && mats.size() > 0) {
            for (T mat : mats) {
                if (mat != null) {
                    try {
                        mat.release();
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    }

    /**
     * 将BufferedImage转换为InputStream
     */
    public static InputStream bufferedImageToInputStream(BufferedImage image) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        return new ByteArrayInputStream(os.toByteArray());
    }

    public static InputStream byteArrayOutputStreamToInputStream(ByteArrayOutputStream os) {
        return new ByteArrayInputStream(os.toByteArray());
    }

    /**
     * 校准文件路径分隔符
     */
    public static String adjustFilePath(String filePath) {
        int m00 = filePath.indexOf("\\");
        int m01 = filePath.indexOf("/");
        if (m00 >= 0) {
            filePath = filePath.replace("\\", File.separator);
        }
        if (m01 >= 0) {
            filePath = filePath.replace("/", File.separator);
        }
        return filePath;
    }

    /**
     * inputStream转outputStream
     */
    public static OutputStream parse(final InputStream in) throws Exception {
        final ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        int ch;
        while ((ch = in.read()) != -1) {
            swapStream.write(ch);
        }
        return swapStream;
    }

    /**
     * outputStream转inputStream
     */
    public static InputStream parse(final OutputStream out) {
        return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());
    }

    public static void createParentFolder(String filePath) throws IOException {
        createParentFolder(new File(filePath));
    }

    public static void createParentFolder(File file) throws IOException {
        File parentFile = file.getParentFile();
        if (!parentFile.isDirectory() && !parentFile.mkdirs()) {
            throw new IOException(String.format("创建文件夹失败 (%s)", parentFile.getPath()));
        }
    }

    private static class DelayedRelease implements Runnable {

        private final TimeUnit timeUnit;
        private final long time;
        private final Closeable[] closeables;

        public DelayedRelease(TimeUnit timeUnit, long time, Closeable[] closeables) {
            this.timeUnit = timeUnit;
            this.time = time;
            this.closeables = closeables;
        }

        @Override
        public void run() {
            try {
                timeUnit.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            close(closeables);
        }
    }
}
